import { WebGLRenderer, WebGLRendererParameters } from "three";
import { StageManager } from "./stage-manager";
import { Stage } from './stage';
import { DomEvent } from './dom-event';
import { Utils } from "../tool/utils";
import { DefaultLoadingScreen } from '../loader/default-loading-screen';


interface IContainerOptions {
    showPerformance?: boolean,
    devicePixelRatio?: number,
    engineOptions?: WebGLRendererParameters,
}


abstract class ThreeContainer {

    public static engine: WebGLRenderer | null = null;
    public static domEvent: DomEvent | null = null;

    public body!: HTMLDivElement;
    public ui!: HTMLDivElement;
    public performance?: HTMLDivElement;
    public canvas!: HTMLCanvasElement;
    public engine!: WebGLRenderer;
    public stageManager!: StageManager;
    public domEvent!: DomEvent;

    private _clientRect!: DOMRect;

    constructor() {
        this.initializeCanvas();
        this.domEvent = new DomEvent(this.body);
        this.domEvent.start();
        ThreeContainer.domEvent = this.domEvent;

        const resizeObserver = new ResizeObserver((entries) => {
            this.onWindowResize();
        });
        resizeObserver.observe(this.canvas as unknown as Element);

        this.initialize3DContent();
    }

    /**
     * 资源加载UI（用于替换默认加载UI）
     * @returns UI（可为null）
     */
    protected loadingUI(): HTMLElement | null {
        return null;
    }

    /**
     * 设置容器参数
     * @returns 容器参数对象
     */
    protected options(): IContainerOptions {
        return {
            showPerformance: false,
            devicePixelRatio: Math.max(Math.min(window.devicePixelRatio, 3), 1),
            engineOptions: {},
        };
    }

    /**
     * 渲染dom类型的UI界面
     */
    protected renderUI(): void {
    }

    /**
     * 初始化舞台
     * @param engine 引擎 
     * @param canvas 画布
     * @param bufferLoader 资源预加载器
     */
    protected abstract initializeStages(engine: WebGLRenderer, canvas: HTMLCanvasElement): Array<Stage>;

    /**
     * 渲染循环
     */
    private renderLoop(): void {
        this.stageManager.render();
    }

    /**
     * 初始化3D内容
     */
    private async initialize3DContent(): Promise<void> {
        if ((window as any).notSupportWebGL !== undefined) {
            alert('failed to setup 3D content, the browser does not support webgl!');
            return;
        }
        delete (window as any).notSupportWebGL;

        const opt = this.options();
        this.engine = new WebGLRenderer({
            ...opt.engineOptions,
            canvas: this.canvas,
        });
        this.engine.setPixelRatio(opt.devicePixelRatio || window.devicePixelRatio);
        this.engine.setSize(this._clientRect.width, this._clientRect.height);
        ThreeContainer.engine = this.engine;
        DefaultLoadingScreen.initialize(this.canvas, '', this.loadingUI());
        this.dispatchPageLoadedEvent();


        DefaultLoadingScreen.displayLoadingUI();
        await Utils.sleep(0);
        this.stageManager = new StageManager();
        const stages = this.initializeStages(this.engine, this.canvas);
        this.stageManager.add(...stages);
        this.setupUI(stages);
        this.stageManager.enable(0);
        this.engine.setAnimationLoop(this.renderLoop.bind(this));

        window.addEventListener('pointerup', this.onUserGesture.bind(this));

        this.setPerformanceMonitor(opt);

    }

    private setPerformanceMonitor(option: IContainerOptions): void {
        if (option.showPerformance) {
            const panel = this.performance!;
            const n = 60;
            const ivn = 1 / 60;
            let t = 0;
            let pt = performance.now();
            let ct = performance.now();
            const ani = () => {
                t++;
                if (t > n) {
                    t = 0;
                    ct = performance.now();
                    const itv = ct - pt;
                    const fps = Math.round(1000 / itv * n);
                    const aft = Math.round(itv / n);
                    const rat = Math.pow(fps * ivn, 2);
                    panel.style.color = 'rgb(' + ((1 - rat) * 255) + ',' + (rat * 255) + ',0)';
                    panel.innerText = 'average frames per second:' + fps + '\n' + 'average frame time:' + aft + 'ms';
                    pt = ct;
                }
                requestAnimationFrame(ani);
            };
            ani();
        }
    }

    /**
     * 创建ui界面(重写实现其它创建规则)
     * @param stages 
     */
    protected setupUI(stages: Stage[]): void {
        stages[0].onPreparedObservable.addOnce(() => {
            this.renderUI();
        });
    }

    /**
     * 用户行为
     */
    protected onUserGesture(): void {
        this.stageManager.onUserGesture();
    }

    /**
     * 窗体变化
     */
    protected onWindowResize(): void {
        const currentRect = this.body.getBoundingClientRect();
        const sizeChange = (currentRect.width !== this._clientRect.width) || (currentRect.height !== this._clientRect.height);
        if (!sizeChange) {
            return;
        }
        this._clientRect = currentRect;

        this.engine.setSize(currentRect.width, currentRect.height);
        if (this.stageManager) this.stageManager.onWindowResize(currentRect);
    }

    /**
     * 初始化容器元素,画布元素及UI容器元素
     */
    private initializeCanvas() {
        const body = document.createElement('div');
        body.id = 'root-container-body';
        body.style.left = '0';
        body.style.right = '0';
        body.style.top = '0';
        body.style.bottom = '0';
        body.style.touchAction = 'none';
        body.style.position = 'absolute';
        body.style.display = 'block';
        body.style.backgroundColor = 'rgba(0,0,0,0)';

        const canvas = document.createElement('canvas');
        canvas.id = 'root-render-canvas';
        canvas.style.width = '100%';
        canvas.style.height = '100%';
        canvas.style.touchAction = 'none';
        canvas.style.position = 'absolute';
        canvas.style.display = 'block';
        canvas.style.outline = '0';
        body.appendChild(canvas);

        const ui = document.createElement('div');
        ui.id = 'root-ui-container';
        ui.style.left = '0';
        ui.style.right = '0';
        ui.style.top = '0';
        ui.style.bottom = '0';
        ui.style.touchAction = 'none';
        ui.style.pointerEvents = 'none';
        ui.style.position = 'absolute';
        ui.style.display = 'block';
        ui.style.zIndex = '100';
        body.appendChild(ui);

        if (this.options().showPerformance) {
            const pf = document.createElement('div');
            pf.id = 'performance-monitor';
            pf.style.right = '0';
            pf.style.top = '0';
            pf.style.width = '250px';
            pf.style.height = '50px';
            pf.style.touchAction = 'none';
            pf.style.pointerEvents = 'none';
            pf.style.position = 'fixed';
            pf.style.zIndex = '1000';
            pf.style.background = 'rgba(0,0,0,0)';
            body.appendChild(pf);
            this.performance = pf;
        }


        this.body = body;
        this.canvas = canvas;
        this.ui = ui;
        this._clientRect = body.getBoundingClientRect();
    }

    /**
     * 发送页面加载完成事件（移除页面加载UI）
     */
    private dispatchPageLoadedEvent(): void {
        const event = new Event('page-loaded');
        window.dispatchEvent(event);
    }
}


export { ThreeContainer };
export type { IContainerOptions };